#!/usr/bin/python3
# -*- coding: utf-8 -*-
# pylint: disable=C0103

""" Window Style Setting
"""

from ctypes import windll, WINFUNCTYPE, POINTER, c_int, cast, pointer
from ctypes.wintypes import DWORD, LONG, HWND, UINT, BOOL, LPLONG, LPCSTR, WPARAM, LPARAM
from constants import (
        GetWindowLongIndex,
        WS_CAPTION, WS_BORDER, WS_EX_CLIENTEDGE, WS_EX_WINDOWEDGE,
        GWL_STYLE, GWL_EXSTYLE
        )

def GetCurrentThreadId():
    """ 获取当前线程 ID. 

    DWORD GetCurrentThreadId()
    """
    prototype = WINFUNCTYPE(DWORD)
    _GetCurrentThreadId = prototype(('GetCurrentThreadId', windll.kernel32))
    return _GetCurrentThreadId()

def GetParent(hwnd: HWND) -> HWND:
    """
    HWND GetParent(HWND hwnd)
    """
    _GetParent = WINFUNCTYPE(HWND, HWND)(('GetParent', windll.user32))
    return _GetParent(hwnd)

IN = 1
LPARAM = LPLONG
WNDENUMPROC = WINFUNCTYPE(BOOL, HWND, LPARAM)
def EnumThreadWindows(
        dwThreadId: DWORD, lpfn: WNDENUMPROC, lParam: LPARAM) ->BOOL:
    """ 
    BOOL EnumThreadWindows(
      [in] DWORD       dwThreadId,
      [in] WNDENUMPROC lpfn,
      [in] LPARAM      lParam
    );
    """
    prototype = WINFUNCTYPE(BOOL, DWORD, WNDENUMPROC, LPARAM)
    # OUT = 2
    paramflags = (IN, 'dwThreadId'), (IN, 'lpfn'), (IN, 'lParam')
    _EnumThreadWindows = prototype(('EnumThreadWindows', windll.user32), paramflags)
    return _EnumThreadWindows(dwThreadId, WNDENUMPROC(lpfn), lParam)

def FindWindowEx(hWndParent: HWND, hWndChildAfter: HWND,
        lpszClass: LPCSTR, lpszWindow: LPCSTR) -> HWND:
    """
    HWND FindWindowExA(
      [in, optional] HWND   hWndParent,
      [in, optional] HWND   hWndChildAfter,
      [in, optional] LPCSTR lpszClass,
      [in, optional] LPCSTR lpszWindow
    );
    """
    prototype = WINFUNCTYPE(HWND, HWND, HWND, LPCSTR, LPCSTR)
    paramflags = (
            (IN, 'hWndParent'),
            (IN, 'hWndChildAfter'),
            (IN, 'lpszClass'),
            (IN, 'lpszWindow'))
    _FindWindowEx = prototype(('FindWindowExA', windll.user32), paramflags)
    return _FindWindowEx(hWndParent, hWndChildAfter, lpszClass, lpszWindow)

def GetWindowLong(hWnd: HWND, nIndex: GetWindowLongIndex) -> LONG:
    """ 获取窗口属性。

    LONG GetWindowLongA(
      [in] HWND hWnd,
      [in] int  nIndex
    );
    """
    # pylint: disable=C0103
    _GetWindowLong = WINFUNCTYPE(LONG, HWND, c_int)(('GetWindowLongA', windll.user32))
    return _GetWindowLong(hWnd, nIndex)

def SetWindowLong(hWnd: HWND, nIndex: int, dwNewLong: LONG) -> LONG:
    """ 设置窗口属性。

    LONG SetWindowLongA(
      [in] HWND hWnd,
      [in] int  nIndex,
      [in] LONG dwNewLong
    );
    """
    _SetWindowLong = WINFUNCTYPE(LONG, HWND, c_int, LONG)(('SetWindowLongA', windll.user32))
    return _SetWindowLong(hWnd, nIndex, dwNewLong)

def IsZoomed(hWnd: HWND = None) -> BOOL:
    """
    BOOL IsZoomed(
      [in] HWND hWnd
    );
    """
    hWnd = hWnd or GetTopWindow()
    _IsZoomed = WINFUNCTYPE(BOOL, HWND)(('IsZoomed', windll.user32))
    return _IsZoomed(hWnd)

LRESULT = LONG
def SendMessage(hWnd: HWND, uMsg: UINT, wParam: WPARAM, lParam:
        LPARAM=pointer(LONG(0))) -> LRESULT:
    """
    LRESULT SendMessage(
      [in] HWND   hWnd,
      [in] UINT   Msg,
      [in] WPARAM wParam,
      [in] LPARAM lParam
    );
    """
    prototype = WINFUNCTYPE(LRESULT, HWND, UINT, WPARAM, LPARAM)
    _SendMessage = prototype(('SendMessageA', windll.user32))
    return _SendMessage(hWnd, uMsg, wParam, lParam)


#################
# user defined :
#################

def EnumThreadWndProc(hwnd: HWND, lParam: LPARAM) -> BOOL:
    """ WindowProc for GetTopWindow. """
    parent = GetParent(hwnd)
    if parent:
        pWnd = cast(lParam, POINTER(HWND))
        pWnd.contents.value = parent
        return False
    return True


def GetTopWindow(dwThreadId: DWORD=None) -> HWND:
    """ return top window handle. """
    hWnd = HWND(0)
    lParam = cast(pointer(hWnd), LPLONG)
    dwThreadId = dwThreadId or GetCurrentThreadId()
    notFound = EnumThreadWindows(dwThreadId, EnumThreadWndProc, lParam)
    return None if notFound else hWnd

def GetVimTextArea() -> HWND:
    """ 返回 Vim 编辑区域所在的窗口. """
    return FindWindowEx(GetTopWindow(), HWND_TOP, b"VimTextArea", b"Vim text area")

def Restore(hWnd: HWND = None):
    """ 还原窗口 """
    hWnd = hWnd or GetTopWindow()
    if IsZoomed(hWnd):
        lParam = LONG(0)
        SendMessage(hWnd, WM_SYSCOMMAND, SC_RESTORE, byref(lParam))
        print("Is Zoomed & Restored")
    else:
        print("Not Zoomed")

def HasCaption(hWnd: HWND = None) -> BOOL:
    """ 是否有标题栏  """
    hWnd = hWnd or GetTopWindow()
    return GetWindowLong(hWnd, GWL_STYLE) & WS_CAPTION.value != 0

def RemoveCaption(hWnd: HWND = None):
    """ 移除窗口的标题栏 """
    hWnd = hWnd or GetTopWindow()
    if HasCaption(hWnd):
        style = GetWindowLong(hWnd, GWL_EXSTYLE)
        SetWindowLong(hWnd, GWL_STYLE, style & ~WS_CAPTION.value)

def HasBorder(hWnd: HWND = None) -> BOOL:
    """ 是否有边框 """
    hWnd = hWnd or GetTopWindow()
    style = GetWindowLong(hWnd, GWL_EXSTYLE)
    return style & WS_BORDER.value != 0

def RemoveBorder(hWnd: HWND = None):
    """ 移除窗口的边框 """
    hWnd = hWnd or GetTopWindow()
    if HasBorder(hWnd):
        style = GetWindowLong(hWnd, GWL_EXSTYLE)
        SetWindowLong(hWnd, GWL_STYLE, style & ~ WS_BORDER.value)

def RemoveEdge(hWnd: HWND = None):
    """ 移除窗口的边缘 """
    SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_STYLE) & ~ WS_EX_CLIENTEDGE.value)
    SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_STYLE) & ~ WS_EX_WINDOWEDGE.value)


if __name__ == '__main__':
    hTop = GetTopWindow()
    assert HasCaption(hTop)
    RemoveBorder(hTop)
    RemoveCaption(hTop)
    # Restore(hWnd)
